package cc.android.testapp.xp;

import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;

import android.app.Application;
import android.app.Instrumentation;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.content.res.XModuleResources;
import android.util.Log;
import android.view.LayoutInflater;

import java.lang.reflect.Modifier;
import java.util.ArrayList;

import cc.android.testapp.cfg.ConfigHelper;
import cc.android.testapp.util.CLog;
import cc.android.testapp.cfg.Config;
import cc.android.testapp.xp.hooker.adblock.util.XHook;
import cc.commons.util.reflect.ClassUtil;
import cc.commons.util.reflect.MethodUtil;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.IXposedHookZygoteInit;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XC_MethodReplacement;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

public class MainHook implements IXposedHookLoadPackage, IXposedHookZygoteInit {

    private static final ArrayList<HookBase> mSubHook = new ArrayList<>();
    private static boolean mInit = false;

    public MainHook() {
        for (Class<?> sClazz : XPTool.getPackgeClass(MainHook.class.getClassLoader(), "cc.android.testapp.xp.hooker", true)) {
            if (HookBase.class.isAssignableFrom(sClazz) && !Modifier.isAbstract(sClazz.getModifiers())) {
                mSubHook.add((HookBase) ClassUtil.newInstance(sClazz));
            }
        }

        CLog.IN_XPOSED = true;
    }

    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam pParam) {
        try {
            if (!pParam.isFirstApplication || !pParam.processName.equals(pParam.packageName))
                return;

            if (pParam.packageName.equals(Config.MODULE_PACKAGE_NAME)) {
                CLog.log("Hook to self, change xposed model enable stats");
                findAndHookMethod(Config.MODULE_PACKAGE_NAME + ".ui.MainActivity", pParam.classLoader
                        , "isModuleEnabled", XC_MethodReplacement.returnConstant(true));
                return;
            }

            ClassLoader tLoader = Application.class.getClassLoader();
            XHook.HookMethod tHookM = new XHook.HookMethod() {
                @Override
                public void onHook(XC_MethodHook.MethodHookParam pMParam, Object pThis, Object... pParams) {
                    try {
                        Application tApp = (Application) ((pMParam.thisObject instanceof Application) ?
                                pMParam.thisObject : (pMParam.args[0] instanceof Application) ? pMParam.args[0] : null);
                        if (tApp == null) return;

                        hookToApp(pParam, tApp);
                    } catch (Throwable e) {
                        Log.e(CLog.TAG, "Error on hook to app", e);
                    }
                }
            };
            XHook.with(tLoader).atClass(ContextWrapper.class)
                    .method("attachBaseContext").args(Context.class).hookAfter(tHookM);
            XHook.with(tLoader).atClass(Application.class)
                    .method("onCreate").hookBefore(tHookM);

        } catch (Throwable exp) {
            Log.e(CLog.TAG, "error on hook main stage", exp);
        }
    }

    public static void hookToApp(XC_LoadPackage.LoadPackageParam pMParam, Application pApp) {
        if (mInit) return;
        mInit = true;

        //客户端禁止保存配置
        Config.AllowSave = false;
        //2.注册远端配置更新广播接收器
        pApp.registerReceiver(new ConfigHelper(), new IntentFilter(ConfigHelper.MSG_UPDATE_CONFIG));
        //3.拉取远端配置至缓存
        ConfigHelper.init(pApp);

        for (HookBase sHook : mSubHook) {
            try {
                HookBase.hook(pMParam, sHook, pApp);
            } catch (Throwable e) {
                Log.e(CLog.TAG, "Error on prepare hook to " + sHook.getModuleName(), e);
            }
        }


        Class<?> tClazz = LayoutInflater.class;
        XposedBridge.hookMethod(MethodUtil.getMethodIgnoreParam(tClazz, "getParserStateDescription", true).oneGet()
                , new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        param.setResult("233");
                    }
                });

    }

    public static Resources TAResource;

    @Override
    public void initZygote(IXposedHookZygoteInit.StartupParam startupParam) {
        TAResource = XModuleResources.createInstance(startupParam.modulePath, null);
    }

}
